﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Schema;

using XCommunity.Documents;
using XCommunity.Forums.Views;
using XCommunity.Frames;
using XCommunity.Members;
using XCommunity.Users;
using XCommunity.Views;

namespace XCommunity.Forums
{

  /// <summary>
  /// 论坛系统的一个实现
  /// </summary>
  public class ForumsSystem : ICommunitySystem
  {

    /// <summary>
    /// 创建论坛系统的实例，不应从用户代码中调用。
    /// </summary>
    /// <param name="context">社区上下文信息</param>
    public ForumsSystem( ICommunityContext context )
    {
      Context = context;
      UserService = new ForumsUserService( this );

      ViewService = new ForumsViewService( this );


      configurationSchemas = LoadConfigurationSchemaSet();
    }


    private XmlSchemaSet LoadConfigurationSchemaSet()
    {
      var schema = new XmlSchemaSet();

      Type xcommunityType = typeof( ICommunitySystem );
      schema.Add( null, XmlReader.Create( Assembly.GetAssembly( xcommunityType ).GetManifestResourceStream( xcommunityType, "XCommunity.xsd" ) ) );


      Type forumsType = typeof( ForumsSystem );
      schema.Add( null, XmlReader.Create( Assembly.GetAssembly( forumsType ).GetManifestResourceStream( forumsType, "Forums.xsd" ) ) );

      schema.Compile();

      return schema;
    }


    /// <summary>
    /// 社区上下文
    /// </summary>
    public ICommunityContext Context
    {
      get;
      private set;
    }


    /// <summary>
    /// 用户服务
    /// </summary>
    public ForumsUserService UserService
    {
      get;
      private set;
    }

    /// <summary>
    /// 视图服务
    /// </summary>
    public ForumsViewService ViewService
    {
      get;
      private set;
    }

    /// <summary>
    /// 成员服务
    /// </summary>
    public MemberService MemberService
    {
      get;
      private set;
    }


    /// <summary>
    /// 提供一个数据访问适配器用于直接访问论坛的数据。
    /// </summary>
    public ForumsDataProvider DataProvider
    {
      get;
      private set;
    }


    /// <summary>
    /// 初始化论坛系统
    /// </summary>
    public virtual void Init()
    {

      _configuration = LoadConfiguration();

      LoadPlugins();
      InitPlugins();


      UserService.Init();
      LoadFrames();

      ViewService.Init();

    }


    /// <summary>
    /// 加载所有论坛插件
    /// </summary>
    protected virtual void LoadPlugins()
    {
      _plugins = Configuration.Root.Element( ForumsSystem.XF + "plugins" ).Elements().Select( element => LoadPlugin( element ) ).ToArray();
    }


    /// <summary>
    /// 从配置文件中加载插件实例
    /// </summary>
    /// <param name="element"></param>
    /// <returns></returns>
    protected virtual ICommunityPlugin LoadPlugin( XElement element )
    {

      var typeAttribute = element.Attribute( "type" );
      var type = Type.GetType( typeAttribute.Value );

      if ( type == null )
        throw new ConfigurationErrorException( string.Format( "找不到类型 \"{0}\"", typeAttribute.Value, typeAttribute ) );

      string name = element.Attribute( "name" ).Value;
      string prefix = element.Attribute( "prefix" ).Value;

      var plugin = (ICommunityPlugin) Activator.CreateInstance( type );

      plugin.Loaded( name, prefix );

      return plugin;

    }

    protected virtual void InitPlugins()
    {
      foreach ( ICommunityPlugin plugin in Plugins )
      {
        plugin.Init( Context );
      }

      foreach ( IForumsPlugin plugin in Plugins.OfType<IForumsPlugin>() )
      {
        plugin.Init( this );
      }

    }




    private ICommunityPlugin[] _plugins;

    /// <summary>
    /// 所有配置的插件实例
    /// </summary>
    public ICommunityPlugin[] Plugins
    {
      get
      {
        if ( _plugins == null )
          throw new InvalidOperationException();

        return _plugins;
      }
    }


    private readonly XmlSchemaSet configurationSchemas;

    private XDocument _configuration;

    /// <summary>
    /// 获取配置文档
    /// </summary>
    public virtual XDocument Configuration
    {
      get
      {
        if ( _configuration == null )
          throw new InvalidOperationException();//UNDONE 异常详细信息
        return _configuration;
      }
    }


    /// <summary>
    /// 加载配置文件
    /// </summary>
    /// <returns></returns>
    protected virtual XDocument LoadConfiguration()
    {

      var settings = new XmlReaderSettings()
      {
        Schemas = configurationSchemas,
        ProhibitDtd = true,
        CloseInput = true,
        ConformanceLevel = ConformanceLevel.Document,
        ValidationType = ValidationType.Schema,
      };

      using ( FileStream stream = new FileStream( @"C:\XCommunity\Forums.xml", FileMode.Open, FileAccess.Read ) )
      {
        using ( XmlReader reader = XmlReader.Create( stream, settings ) )
        {
          return XDocument.Load( reader, LoadOptions.SetLineInfo );
        }
      }
    }



    private Dictionary<string, ForumsBoardFrame> _boards;


    /// <summary>
    /// 所有版面列表
    /// </summary>
    protected virtual IDictionary<string, ForumsBoardFrame> Boards
    {
      get
      {
        if ( _boards == null )
          throw new InvalidOperationException();//UNDONE 异常详细信息
        return _boards;
      }
    }


    /// <summary>
    /// 从配置文件中加载框架信息
    /// </summary>
    /// <returns></returns>
    protected virtual void LoadFrames()
    {
      var boards = LoadBoards();
      _boards = boards.ToDictionary( item => item.BoardId );
    }


    /// <summary>
    /// 从配置文件中加载所有版面信息
    /// </summary>
    /// <returns></returns>
    protected virtual ForumsBoardFrame[] LoadBoards()
    {
      List<ForumsBoardFrame> boards = new List<ForumsBoardFrame>();

      foreach ( var boardElement in Configuration.Root.Element( XF + "frames" ).Elements( XF + "board" ) )
      {

        var typeAttribute = boardElement.Attribute( "type" );

        var boardType = Type.GetType( typeAttribute.Value );

        ForumsBoardFrame boardFrame = CreateBoard( boardType, boardElement );

        boards.Add( boardFrame );

      }

      return boards.ToArray();
    }


    /// <summary>
    /// 通过配置文件创建一个版面对象
    /// </summary>
    /// <param name="boardType">版面对象类型</param>
    /// <param name="configElement">版面配置节点</param>
    /// <returns></returns>
    private ForumsBoardFrame CreateBoard( Type boardType, XElement configElement )
    {

      var args = new object[] { this, configElement };
      return (ForumsBoardFrame) boardType.GetConstructor( Type.GetTypeArray( args ) ).Invoke( args );
    }


    /// <summary>
    /// 发起一个话题
    /// </summary>
    /// <param name="document">发起话题的文档</param>
    /// <param name="board">发起话题的版面</param>
    /// <returns></returns>
    public virtual GeneralTopicFrame LaunchTopic( Document document, ForumsBoardFrame board )
    {
      if ( !document.Meta.Saved )
        throw new ArgumentException( "不能发布未保存的文档", "document" );

      TopicMeta meta = CreateTopicMeta( document );


      FrameService service = Context.FrameService;

      int topicId = service.TopicPersister.SaveTopic( meta );

      GeneralTopicFrame topic = new GeneralTopicFrame( service, meta, this );

      topic.LaunchedFrom( document );

      service.TopicPersister.SaveTopic( topic );

      /*
      service.TopicMetaCache.AddItem( meta );
      service.TopicFrameCache.AddItem( topic );
      */
      return topic;
    }

    /// <summary>
    /// 创建一个话题的元数据
    /// </summary>
    /// <param name="document"></param>
    /// <returns></returns>
    protected virtual TopicMeta CreateTopicMeta( Document document )
    {
      DateTime publishDate = DateTime.UtcNow;

      TopicMeta meta = new TopicMeta()
      {
        Title = document.Meta.Caption,
        Launcher = document.Meta.Author,
      };

      return meta;
    }


    /// <summary>
    /// 获取版面框架对象
    /// </summary>
    /// <param name="boardId"></param>
    /// <returns></returns>
    public virtual ForumsBoardFrame GetBoard( string boardId )
    {
      return Boards[boardId];
    }


    /// <summary>
    /// 获取话题被管辖的版块
    /// </summary>
    /// <param name="topic">话题框架对象</param>
    /// <returns>具有此话题管辖权的版块</returns>
    public virtual ForumsBoardFrame GetJurisdictionBoard( GeneralTopicFrame topic )
    {
      string boardId = (string) ForumsDataProvider.DbUtility.ExecuteScalar( "SELECT BoardId FROM ForumsTopic WHERE ID = ( SELECT MIN( ID ) FROM ForumsTopic WHERE TopicId = {0} )", topic.TopicId );

      return GetBoard( boardId );
    }





    /// <summary>
    /// 论坛配置命名空间
    /// </summary>
    public static readonly XNamespace XF = "http://xcommunity.net/XForums/Configuration";



    private static readonly Guid _guid = new Guid( "{2F372E46-6E86-4e72-872F-DCC677382860}" );

    Guid ICommunitySystem.Guid
    {
      get { return _guid; }
    }

    IUserService ICommunitySystem.UserService
    {
      get { return UserService; }
    }


  }
}